package com.ruoyi.web.controller.iot.utils.jar;

import com.lcc.iot.service.IotBaseHandlerService;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class Demo2 {

	public static String jarFilePath = "G:\\code\\ProjectJava\\lamp_Iot\\iot-lampv1\\target\\iot-lampv1-sdk-1.0.0-SNAPSHOT.jar";

	public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
		// 自定义class load 加载器
		URLClassLoader cl = loadAllJar(jarFilePath);
		// 加载所有的class,并返回指定service的实现类
		List<Class<?>> cls = getClassListByInterface(jarFilePath, cl, IotBaseHandlerService.class);
		Class<?> c1 = cls.get(0);
		c1.getMethod("test").invoke("111");
		IotBaseHandlerService iot = (IotBaseHandlerService) cls.get(0).newInstance();
		// 创建实例 并加载到内存执行aa
		cls.stream().forEach(cz->{
			loadAndInstanceClass(cz.getName(), cl);
		});
	}


	/**
	 * 加载类和实例化类
	 * @param clazzName
	 * @param classLoader
	 */
	private static void loadAndInstanceClass(String clazzName, ClassLoader classLoader) {
		try {
			// 需要使用其他的classLoader加载
			Class<?> forName = classLoader.loadClass(clazzName);
			System.out.println(forName.getName());
			try {
				IotBaseHandlerService iot = (IotBaseHandlerService) forName.newInstance();
				System.out.println(iot.deCode("asdasdasdas"));
				System.out.println(iot.encrypt("asdasdasdas"));
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static List<Class<?>> getClassListByInterface(String url, ClassLoader classLoader, Class<?> clazz) {
		List<Class<?>> classList = new ArrayList<>();
		if (!clazz.isInterface()) {
			return classList;
		}
		List<String> classNames = new ArrayList<>();
		try (JarFile jarFile = new JarFile(url)) {
			Enumeration<JarEntry> entries = jarFile.entries();
			while (entries.hasMoreElements()) {
				JarEntry jarEntry = entries.nextElement();
				String entryName = jarEntry.getName();
				if (entryName != null && entryName.endsWith(".class")) {
					entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
					classNames.add(entryName);
				}
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

		if (classNames.size() > 0) {
			for (String className : classNames) {
				try {
					Class<?> theClass = classLoader.loadClass(className);
					if (clazz.isAssignableFrom(theClass) && !theClass.equals(clazz)) {
						classList.add(theClass);
					}
				} catch (ClassNotFoundException e) {
					throw new RuntimeException(e);
				}
			}
		}
		return classList;
	}

	public static URLClassLoader loadAllJar(String url) {
		URLClassLoader classLoader = new URLClassLoader(new URL[]{}, ClassLoader.getSystemClassLoader());
		Method method;
		try {
			method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
			if (!method.isAccessible()) {
				method.setAccessible(true);
			}
			method.invoke(classLoader, new URL("file:" + url));
		} catch (NoSuchMethodException | MalformedURLException | InvocationTargetException | IllegalAccessException e) {
			throw new RuntimeException(e);
		}
		// write to local lib
		try {
			String jarResourceStr = "jar:file:" + url + "!/";
			URL libUrl = new URL(jarResourceStr);
			URLConnection urlConnection = libUrl.openConnection();
			if (urlConnection instanceof JarURLConnection) {
				JarURLConnection jarURLConnection = (JarURLConnection) urlConnection;
				JarFile jarFile = jarURLConnection.getJarFile();
				Enumeration<JarEntry> entries = jarFile.entries();
				String parentPath = Paths.get(url).getParent().toString();
				File libDir = Paths.get(parentPath, "lib").toFile();
				libDir.deleteOnExit();
				boolean isSuccess = libDir.mkdirs();
				if (!isSuccess) {
					System.out.println("create lib dir fail");
				}
				while (entries.hasMoreElements()) {
					JarEntry jarEntry = entries.nextElement();
					String entryName = jarEntry.getName();
					if (entryName.endsWith(".jar")) {
						try (InputStream inputStream = classLoader.getResourceAsStream(entryName)) {
							File target = new File(parentPath, entryName);
							FileUtils.copyInputStreamToFile(inputStream, target);
							method.invoke(classLoader, new URL("file:" + target.getPath()));
						} catch (IOException e) {
							throw new RuntimeException(e);
						} catch (InvocationTargetException e) {
							throw new RuntimeException(e);
						} catch (IllegalAccessException e) {
							throw new RuntimeException(e);
						}
					}
				}
			}

		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return classLoader;
	}
}
